Add judge portal for hackathon submission review#4
Merged
DeeprajPandey merged 39 commits intomainfrom Feb 3, 2026
Merged
Conversation
…otes - Verify team has submitted (status=3) before allowing judging - Add optimistic locking to prevent concurrent update conflicts - Return 409 Conflict when another judge has updated the record - Frontend sends expected_updated_at and handles conflict response
- Extract SDG color mapping to constant to eliminate duplication - Add unsaved changes confirmation when closing/switching rows - Add 300ms debounce to search input for better performance - Fix event handler types with proper ChangeEvent typing - Remove unused imports (NeoCard, NeoBadge)
Hackathon scale doesn't require pagination - load up to 1000 teams in one request for simpler client-side filtering.
- Display 25 items per page with prev/next navigation - Show "Showing X-Y of Z" info - Reset to page 1 when filters change
- Normalizes scores to one row per judge per team per pass - Migrates existing judging_notes data as first reviewer entries - Keeps judging_notes for final_decision (avoids migration complexity)
- Fetches scores from new judging_scores table - Computes per-reviewer averages and pass aggregates - Implements consensus logic (majority-based voting) - Maintains backward compatibility with legacy judgingNotes fields
- PATCH /api/judge/scores/[teamId] for saving reviewer scores - Enforces reviewer caps (4 for Pass 1, 3 for Pass 2) - Upserts on (team_id, judge_id, pass) unique constraint - Returns updated scores and aggregate after save
- Pass 1/2 scores now handled by /api/judge/scores/[teamId] - Returns helpful error if legacy fields are sent
- Split-view layout: team list (left) + detail panel (right) - Mobile-responsive with list/detail navigation (<900px breakpoint) - Multi-reviewer display showing all judges' scores and notes - Aggregate scores with consensus voting (majority-based) - "Needs Reviews" filter to find teams below reviewer cap - Keyboard navigation (↑/↓ or j/k) for efficient judging - Unsaved changes warning before switching teams
- Move 'Your Review' form above 'Other Reviews' section - Primary action (scoring) now visible without scrolling - Filter current user from 'Other Reviews' list (shown separately)
- Sticky save bar at bottom of detail panel, always visible - Success toast with 2.5s auto-dismiss on save - Proper timeout cleanup on unmount - Bar hidden when cap reached and user can't save
- Quick filter buttons for P1/P2 where current judge hasn't scored - Toggle buttons show active state with blue highlight - Combines with existing filters using AND logic
- Larger padding, thicker borders, bolder font weight - Added icons: ✓ Yes, ◐ Maybe/Waitlist, ✗ No, ★ Finalist - Uppercase text transform for emphasis - Hover lift effect with shadow animation
- Collapse dropdown filters behind toggle by default - Keep search and quick filter buttons always visible - Show active filter count badge when filters applied - Reduces filter section height when collapsed
- Final tab shows indicator (✓ for decided, — for pending) - MAYBE/Waitlist buttons use darker orange for better contrast - Team list items more compact with visual separation - Filters toggle looks like a clickable button - HW badge moved before SDG badge in team list - SDG badges slightly smaller
Project title shown as main heading, team name as "by" subtitle. Clearer visual hierarchy for judges reviewing submissions.
Allows bulk-marking multiple teams as finalist/waitlist/not_selected
in a single API call instead of updating one at a time.
- POST /api/judge/bulk-decision
- Accepts { teamIds: string[], decision: 'finalist' | 'waitlist' | 'not_selected' }
- Max 500 teams per request
- Auth: SUPERADMIN or JUDGE role required
Export teams and member details (name, email) for finalist or waitlist teams. Returns JSON for frontend CSV conversion. - GET /api/judge/export?decision=finalist|waitlist - Returns team name, project title, and all members - Auth: SUPERADMIN or JUDGE role required
…dge portal Streamlines finalist/waitlist selection workflow: - Select all visible checkbox with indeterminate state - Bulk action buttons: Mark as Finalist/Waitlist, Clear Selection - Export buttons: Download CSV with team + member emails - Track breakdown summary showing teams/people by SDG for finalists/waitlist - Fix: Allow Final tab access when team already has finalDecision set - CSV export properly escapes quotes to prevent injection
… teams - Export not_selected now returns all submitted teams NOT marked as finalist/waitlist - Added "Mark as Not Selected" bulk action button - Added "Not Selected CSV" export button
4d269f4 to
1424893
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
Adds a judge portal at
/judgefor reviewing hackathon submissions with two-pass scoring, final decisions, bulk actions, and CSV export.Note
Access restricted to admins (role 1) and judges (role 7).
DB changes
Migrations:
20260118000002_create_judging_notes.sql- final decisions table20260118000003_fix_judging_notes.sql- adds moddatetime extension20260120000001_create_judging_scores.sql- multi-reviewer scores tableWarning
Requires
moddatetimePostgreSQL extension. Run migrations before deployment.Features
API endpoints
/api/judge/submissions/api/judge/export?decision=/api/judge/bulk-decision/api/judge/notes/[teamId]/api/judge/scores/[teamId]/api/judge/tally-profile?teamId=/api/judge/team-profiles